/*
 * 22/10/2011 - 11:45
 */
package org.nettyflux.jsonrpc;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.UUID;
import org.nettyflux.communicator.RemoteHost;
import org.nettyflux.mailbox.MailBox;
import org.nettyflux.mailbox.ReceiveListener;
import org.nettyflux.netmessages.MessageReceived;
import org.nettyflux.netmessages.NetworkMessage;
import org.nettyflux.netmessages.SendCommand;
import org.nettyflux.remote.StubStrategy;

/**
 * @author Victor Williams Stafusa da Silva
 */
public class SimpleJsonStubStrategy implements StubStrategy {

    private final JsonMarshal marshal;

    public SimpleJsonStubStrategy() {
        this(new JsonMarshal());
    }

    public SimpleJsonStubStrategy(JsonMarshal marshal) {
        this.marshal = marshal;
    }

    @Override
    public Object handle(
            MailBox<NetworkMessage> mailbox,
            String objectName,
            RemoteHost host,
            Object proxy,
            Method method,
            Object[] args)
            throws Throwable
    {
        Command c = new Command();
        c.setParameters(args);
        c.setObjectName(objectName);
        c.setOperation(method.toGenericString());
        c.setCommandId(UUID.randomUUID().toString());
        byte[] serializedCommand = marshal.write(c);
        ResponseListener listener = new ResponseListener(host, c);
        mailbox.addReceiveListener(MessageReceived.class, listener);
        mailbox.postMessage(SendCommand.create(host, serializedCommand));

        synchronized (listener) {
            while (listener.response == null) {
                listener.wait();
            }
        }

        CommandReply reply = listener.response;
        if (!reply.isSuccess()) {
            throw (Throwable) reply.getResult();
        }
        return reply.getResult();
    }

    private class ResponseListener implements ReceiveListener<MessageReceived<?>> {

        private final Command command;
        private final RemoteHost host;
        private volatile CommandReply response;

        public ResponseListener(RemoteHost host, Command command) {
            this.command = command;
            this.host = host;
        }

        public CommandReply getResponse() {
            return response;
        }

        @Override
        public void receive(MessageReceived<?> message) {
            if (!message.getSender().equals(host)) return;

            CommandReply reply;
            try {
                reply = marshal.read(CommandReply.class, message.getMessage());
            } catch (IOException e) {
                // Ignore.
                return;
            }

            if (!reply.isCompatibleWith(command)) return;

            response = reply;
            synchronized (this) {
                notify();
            }
        }
    }
}
